home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Ken Long / MissileCmd2.3-p-c / Think C / Rough Port < prev    next >
Encoding:
Text File  |  1994-12-04  |  37.2 KB  |  1,323 lines  |  [TEXT/KAHL]

  1.  
  2. //• C translation from Pascal source file: Missile.p
  3.  
  4. //• From: mrob & world. std. com (Robert P Munafo).
  5. //• Subject : "Missile " source from 1984.
  6.  
  7. //• Recently in comp. sys. mac. programmer there has been some discussion.
  8. //• of early Macintosh programs that still run on current machines ,.
  9. //• and my Missile Command game from mid - 1984 was mentioned.
  10.  
  11. //• This program was written in Lisa Pascal,.
  12. //• the cross - development system that everyone had to use.
  13. //• until Stanford's sumacc came along,.
  14. //• and has the rather charming property of continuing to run on.
  15. //• all new machines that Apple comes out with.
  16. //• I ran it on my own Power Mac 6100 / 60 yesterday just for fun.
  17. //• It 's just as playable as it was on the old 128K Mac.
  18. //• (although the timing for the " Game Over " screen is now way off ,.
  19. //• as I didn 't check TickCount for that).
  20.  
  21. //• I have not done anything to this code ,.
  22. //• like try to port it to any recent Pascal compiler or anything like that.
  23. //• I know that Lisa Pascal , Macintosh Pascal ,.
  24. //• and TML Language Systems Pascal were all reasonably.
  25. //• compatible with each other;.
  26. //• after that I switched over to Think C and lost track.
  27.  
  28. //• The "%title " and " %subtitle " directives were.
  29. //• for the print spooler on the Dartmouth mainframe that I used to.
  30. //• print out listings.
  31.  
  32. //• There seem to be a lot more options in the Game Options dialog.
  33. //• than the actual Missile game provides.
  34. //• I 'm not sure what' s going on with that.
  35. //• - - - - - - - - - - - Cut Here - - - Pascal Source code - - - - - - - - - - -.
  36.  
  37.  
  38. //• %title 'Missile - a game for the Apple Macintosh'.
  39. //• %subtitle 'Overall program description'.
  40. //• $X-.
  41. //• Turns off A.R.s.E.
  42.  
  43. //• main Game;
  44.  
  45. //• Missile -- A Missile Command game for the Macintosh.
  46. //•            by Robert P. Munafo at Dartmouth College.
  47.  
  48. //•    I wrote this to learn a bit about Mac programming. It has a.
  49. //• few 'standard user interface' bugs:.
  50.  
  51. //•    It doesn't update the window during the 'End of round X' and.
  52. //•    'GAME OVER' routines. (It only updates while playing.).
  53. //•    It won't quit when you select "quit" while a desk accessory is.
  54. //•    on screen. Instead, it will wait until you close all the.
  55. //•    desk accessories, then quit.
  56. //•    The cursor looks the same all the time. It should change.
  57. //•    shape to let the user know when he can and cannot fire missiles.
  58.  
  59. //• It also has some missing features (which I hope to add soon):.
  60.  
  61. //•    o Smart Bombs.
  62. //•    o Add an "Ammunition: [xxx]% of normal" item to the dialog box.
  63. //•    o Sound.
  64. //•    o User should be able to pick what round the game starts with.
  65. //•    o High scores list saved on the disk.
  66. //•    o Two-player option.
  67. //•    o The animation needs to be made faster (in some manner).
  68.  
  69.  
  70. //• %subtitle 'USES statements'.
  71.  
  72. #include <Packages.h>
  73.  
  74. //• %subtitle 'Constants'.
  75. enum {
  76.     lastMenu = 4,    //• number of menus.
  77.     appleMenu = 1,    //• menu ID for desk accessory menu.
  78.     fileMenu = 256,    //• menu ID for File menu.
  79.     editMenu = 257,    //• menu ID for the Edit menu.
  80.     gameMenu = 258,    //• menu ID for Game menu.
  81.     str_1_ID = 300,    //• ID for my first string of text.
  82.     str_2_ID = 350,    //• ID for my second string of text.
  83.     box_ID = 450,    //• ID of "about" dialog box.
  84.     box2_id = 451,    //• ID of options box.
  85.  
  86.     fbIdle = 0,
  87.     fbGrow = 1,
  88.     fbShrink = 2,
  89.     maxFb = 60,        //• maximum number of fireballs allowed.
  90.     fbRad = 15,        //• Radius of fireballs.
  91.     fbRadRate = 2,    //• Rate of expansion/contraction of fireballs.
  92.     msIdle = 0,
  93.     msActive = 1,
  94.     msNormal = 1,
  95.     msMirv = 2,
  96.     msSmart = 3,
  97.     maxMs = 20,        //• maximum number of missiles allowed.
  98.     mWidth = 3,        //• Width of missile tracks.
  99.     cityHeight = 40,    //• Distance from bottom up to cities.
  100.     cityWidth = 50,    //• Width of cities.
  101.     BuildHeight = 30,    //• maximum height of buildings.
  102.     buildWidth = 4,    //• Width of buildings.
  103.     nCities = 6    //• Number of cities.
  104. };
  105.  
  106. typedef struct {    //• Data type to describe a fireball.
  107.         Rect bounds;    //• Rectanggle to copy bits into.
  108.         short size;    //• Current radius of fireball.
  109.         short mode;    //• Idle, Growing, or shrinking.
  110.     } fireball;
  111.  
  112. typedef struct {    //• Data type to describe a missile.
  113.         Point start;    //• One endpoint of the missile's path.
  114.         Point pos;    //• Position within path - 0 to 1.
  115.         short dh, dv;    //• Vertical and horizontal speed in pixels.
  116.         short city;    //• Number of the target city.
  117.         short kind;    //• MIRV or normal.
  118.         short mode;    //• Idle or Active.
  119.     } missile;
  120.  
  121.  
  122. MenuHandle myMenus[lastMenu];
  123. Rect screenRect;
  124. Boolean doneFlag;
  125. EventRecord myEvent;
  126. short code, refNum;
  127. WindowRecord wRecord;
  128. WindowPtr myWindow, whichWindow;
  129. StringHandle first_string, second_string;    //• Handles to strings of text for AboutMissile.
  130.  
  131. //• My game variables are here.
  132. BitMap fbBitMap;    //• BitMap for fireball pictures.
  133. short fbBits[3600];    //• Actual bits for fireball pictures.
  134.  
  135. Cursor crosshairs;    //• Crosshairs cursor read in from resource.
  136. Cursor crosshairs2;
  137. CursHandle hCurs, hCurs2;    //• Handle to the cursor.
  138. PatHandle cityPattern;//• Handle to pattern in which to draw cities.
  139. short fieldWidth;    //• Width of the playing field.
  140. short fieldHeight;    //• Height of the playing field.
  141. short i;            //• Loop variable.
  142. fireball fbs[maxFb];    //• Each entry describes one fireball.
  143. missile missiles[maxMs];    //• Each entry describes one missile.
  144. short nMissiles;    //• Number of currently active missiles.
  145. long lastTick;    //• System clock at time of previous update.
  146. Boolean gameOver;    //• Set true when all cities are destroyed.
  147. Boolean pauseFlag;    //• True when game is being paused.
  148. Boolean playing;    //• True except buring bonus points and GAME OVER screens.
  149. Boolean esFlag;    //• True if "GAME OVER" is to be drawn after exiting main loop.
  150. short endDelay;    //• Used to wait a while after end of round.
  151. short citiesLeft;    //• Number of cities still alive.
  152. short bCities;    //• Number of bonus cities they have left.
  153. Boolean cities[nCities];    //• State of each city : false = destroyed.
  154. Boolean cities2[nCities];    //• State of the city before the round began.
  155. short nukeHeight;    //• Vertical position of fireball required to destroy city.
  156. short endv;        //• Y-coordinate of the end of a missile's path.
  157. short mirvRate;
  158. short mirvHeight;    //• Height at which Mirvs MIRV.
  159. short mirvNasty;    //• Percent chance of sub-missile on mirv for each city.
  160. short msSpeed;    //• Vertical speed of missiles, in 1/16ths of a pixel.
  161. short msRate;    //• Percent rate of missile production.
  162. short roundNumber;    //• Number of the current round.
  163. long score;        //• The player's current score.
  164. long disScore;    //• score currently displayed on the screen.
  165. long highScore;    //• The current highest score.
  166. short RoundH;
  167. short scoreH;    //• Horiz. position of text 'score:'.
  168. short scoreNumH;    //• Horiz. position of score.
  169. short statsH;    //• Horiz. position of text 'Enemy left:'.
  170. short statsNumH;
  171. Rect statsRect;
  172. short eLeft;        //• How many enemy missiles are left this round.
  173. short yLeft;        //• How many missiles you (the player) have left.
  174. short eDestroyed;    //• Number of enemy missiles destroyed.
  175. short eDesH;        //• Horiz. position of text 'Enemy Destroyed:'.
  176. short eDesNumH;    //• Horiz. position of # of enemy missiles destroyed.
  177.  
  178. short gameSpeed;    //• Number of ticks between each AdvanFb call.
  179. short startRound;    //• Round to start off with.
  180. Boolean mFlag1;    //• Do missiles aim for dead cities?.
  181. short mFlag2;    //• Extent to which enemy missiles blow up.
  182. Boolean MExists[3];    //• Does this type of missile occur?.
  183. short MPBase[3];    //• Value of missile at first round.
  184. short MPExtra[3]; //•.plus this much each additional round.
  185. short MPoints[3]; //•.is this much in all.
  186.  
  187. void SetUpMenus (void);
  188. void InitFbs (void);
  189. void SetUp (void);
  190. void AboutMissile (void);
  191. Fixed Rnd (Fixed n);
  192. void DrawNumber (long n);
  193. void UpdScore (void);
  194. void DrawScore (void);
  195. void BottomLine (void);
  196. void AllocFB (Point location);
  197. void AdvanFb (void);
  198. void AllocMS (Point position, short city, short kind);
  199. Boolean BlackPixel (Point aPoint);
  200. void AdvanMs (void);
  201. void Centre (Str255 theText, short posV);
  202. void ClearScreen (void);
  203. void DrawCity (short city);
  204. void DrawStuff (void);
  205. short MinMax (Str255 theString, short minimum, short maximum);
  206. void DoGameOptions (void);
  207. void DoCommand (long mResult);
  208. void CheckEvents (void);
  209. void EndScreen (void);
  210. void InitRound (void);
  211. void EndBonus (void);
  212. void PlayGame (void);
  213. void main (void);
  214.  
  215. //• %subtitle 'Init routine for menus'.
  216. void SetUpMenus ()
  217. {
  218.     short i;
  219.  
  220.     myMenus[1] = GetMenu (appleMenu);            //• Load menu from disk.
  221. //• MyMenus[1]^^.MenuData[1] := Chr (AppleSymbol);.
  222. //• Change title to the Apple symbol.
  223.     AddResMenu (myMenus[1], 'DRVR');                //• Add items for desk accessories.
  224.     myMenus[2] = GetMenu (fileMenu);
  225.     myMenus[3] = GetMenu (editMenu);
  226.     myMenus[4] = GetMenu (gameMenu);
  227.     for (i = 1; i <= lastMenu; i++)
  228.         InsertMenu (myMenus[i], 0);
  229.     DrawMenuBar ();
  230. }
  231. //• of SetUpMenus.
  232.  
  233. //• %subtitle 'Init routine for fireball BitMaps'.
  234. //•    This routine is called by the main Init routine, below. It creates a.
  235. //•    bunch of bit images of circles off-screen, which can later be drawn on.
  236. //•    the screen much faster than FrameOval.
  237. void InitFbs ()
  238. {
  239.     Fixed i;
  240.     Rect aRect;        //• Rectangle for drawing FrameOvals.
  241.     BitMap saveBits;    //• To save the current GrafPort while drawing off-screen.
  242.     Rect saveRect;
  243.  
  244.     saveBits = qd.thePort->portBits;
  245.     saveRect = qd.thePort->portRect;
  246.     fbBitMap.rowBytes = 8;
  247.     SetRect (&fbBitMap.bounds, 0, 0, 60, 60);
  248.     qd.thePort->portRect = fbBitMap.bounds;
  249.  
  250.     PenSize (fbRadRate, fbRadRate);
  251.     PenPat (&qd.black);
  252.     PenMode (patCopy);
  253.     SetRect (&aRect, 30, 30, 30, 30);    //• Start with an empty rectangle in the center.
  254.     for (i = 0; i < 14; i++)
  255.     {
  256.         fbBitMap.baseAddr = (Ptr) &fbBits[240 * i + 1];    //• Pick starting address for this picture.
  257.         InsetRect (&aRect, -2, -2);                //• Make rect a bit larger.
  258.         SetPortBits (&fbBitMap);
  259.         EraseRect (&qd.thePort->portBits.bounds);        //• Erase to all white.
  260.         FrameOval (&aRect);                        //• Draw the circle.
  261.     }
  262.     SetPortBits (&saveBits);        //• Return to normal screen drawing.
  263.     qd.thePort->portRect = saveRect;
  264. }
  265. //• of InitFbs.
  266.  
  267. //• %subtitle 'Init routine'.
  268. void SetUp ()
  269. {
  270.     Rect wRect;    //• Window Rectangle.
  271.     short i;
  272.                     //•    Macintosh System initialization.
  273.     InitGraf (&qd.thePort);    //• Quickdraw.
  274.     InitFonts ();        //• Font Manager.
  275.     InitWindows ();        //• Window Manager.
  276.     InitMenus ();            //• Menu Manager.
  277.     TEInit ();            //• TextEdit.
  278.     InitDialogs (0L);    //• Dialog manager.
  279.     InitCursor ();        //• Cursor handler.
  280.     InitAllPacks ();        //• Package Manager.
  281.  
  282.     SetUpMenus ();        //•    My routine to insert the menus.
  283. //     hCurs = *GetCursor (256);    //• Load the Crosshairs cursor that I use in the game.
  284. //     hCurs2 = *GetCursor (257);    //• Load the Crosshairs cursor that I use in the game.
  285. //     crosshairs = hCurs[0][0];
  286. //     crosshairs2 = hCurs2[0][0];
  287. //     SetCursor (crosshairs);
  288. //    cityPattern = GetPattern (256);
  289.  
  290.     screenRect = qd.screenBits.bounds;    //• Don't actually use this, but might later.
  291.  
  292.     doneFlag = false;                    //• This flag is set to False when user selects 'Quit'.
  293.     highScore = 0;
  294.  
  295.     gameSpeed = 8;                    //• Set up all the user-settable options.
  296.     startRound = 0;
  297.     mFlag1 = true;
  298.     mFlag2 = 1;
  299.     for (i = 1; i <= 3; i++)
  300.     {
  301.         MExists[i] = true;
  302.         MPBase[i] = 10 * i;
  303.         MPExtra[i] = 10 * i;
  304.     }
  305.  
  306.     //• myWindow := GetNewWindow (256, &wRecord, (WindowPtr)-1L);.
  307.     wRect = screenRect;
  308.     wRect.top = 20;
  309.     myWindow = NewWindow (&wRecord, &wRect, 
  310.                          "\pMissile Command by Robert Munafo", 
  311.                          true, 0, (WindowPtr)-1L, false, 0);                
  312.  
  313.     SetPort (myWindow);    //• Might as well play the game in a window.
  314.  
  315.     //• These variables are used by the game.
  316.     fieldWidth = myWindow->portRect.right;    
  317.     fieldHeight = myWindow->portRect.bottom;
  318.  
  319.     TextFont (0);
  320.     TextFace (0);
  321.  
  322.     InitFbs ();            //• Set up the fireball BitMaps.
  323.  
  324.     second_string = GetString (str_2_ID); //• Get strings from resource.
  325.     first_string = GetString (str_1_ID);
  326.  
  327.     FlushEvents (everyEvent, 0);
  328. }
  329.  
  330. //• %Subtitle 'Gets a text string'.
  331.  
  332. //•    This routine conducts a dialog to get a text string from the.
  333. //•    user. The function value returned indicates whether the user stopped by entering.
  334. //•    OK or cancel. The ID is the dialog ID number to use and Text is the.
  335. //•    text they typed in. To get the text, we display the dialog window,.
  336. //•    get the handle of the edit text and execute a small event loop wich waits.
  337. //•    until one of the two buttons is pressed. When done, we dispose of the dialogue.
  338. //•    window, set the flag depending on the means of exiting and return the text.
  339. //•    The predefined dialogue window has the following items in it:.
  340.  
  341. //• Item Number            Item.
  342. //•    1                OK button, Enabled.
  343. //•    2                Cancel button, Enabled.
  344. //•    3                EditText area, Enabled.
  345. //•    4                StatText area, Disabled (Prompt).
  346. //•    5.                Anything else, Disabled.
  347. //• *)
  348.  
  349. //• the actual function has been deleted.
  350.  
  351. //• %subtitle '"About Missile" routine'.
  352. //• This routine is nearly identical to Jason Ansley's About_Windows routine.
  353. void AboutMissile ()
  354. {
  355.     short item_chosen;    //• which botton was hit (I only have one so it doesn't really matter).
  356.     short item_type;    //• type of item in res. def. file (needed to pass as parameter).
  357.     Handle first_handle, second_handle;    //• handles to my items.
  358.     DialogPtr the_dialog;    //• a pointer to my dialog box.
  359.     Rect box;            //• the display rectangle of the item.
  360.  
  361.     the_dialog = GetNewDialog (box_ID, 0L, (WindowPtr)-1L);    //• get dialog box from resource file.
  362.  
  363.     GetDItem (the_dialog, 2, &item_type, &first_handle, &box);    //• get text format info from file.
  364.     GetDItem (the_dialog, 3, &item_type, &second_handle, &box);
  365.  
  366.     SetIText (first_handle, &first_string[0][0]);    //• set the lines of text into the box.
  367.     SetIText (second_handle, &second_string[0][0]);
  368.  
  369.     ModalDialog (0L, &item_chosen);    //• operate the box.
  370.     DisposDialog (the_dialog);    //• get rid of the box now that I'm done with it.
  371. }
  372.  
  373. //• %subtitle 'My own random number routine'.
  374. Fixed Rnd (Fixed n)
  375. {
  376.     return ((abs (Random ()) % n) + 1);
  377. }
  378.  
  379. //• %subtitle 'DrawNumber - converts number to string and draws it on the screen'.
  380. void DrawNumber (long n)
  381. {
  382.     Str255 s;
  383.  
  384.     NumToString (n, s);
  385.     DrawString (s);
  386. }
  387.  
  388. //• %subtitle 'Update the score at the bottom of the screen'.
  389. void UpdScore ()
  390. {
  391.     TextMode (srcBic);
  392.     MoveTo (scoreNumH, fieldHeight - 5);
  393.     TextSize (24);
  394.     DrawNumber (disScore);
  395.  
  396.     TextMode (srcOr);
  397.     MoveTo (scoreNumH, fieldHeight - 5);
  398.     DrawNumber (score);
  399.     disScore = score;
  400.  
  401.     EraseRect (&statsRect);
  402.     TextMode (srcOr);
  403.     MoveTo (statsNumH, fieldHeight - 20);
  404.     TextSize (12);
  405.     DrawNumber (eLeft);
  406.     MoveTo (statsNumH, fieldHeight - 5);
  407.     DrawNumber (yLeft);
  408.     TextMode (srcCopy);
  409.     MoveTo (eDesNumH, fieldHeight - 20);
  410.     DrawNumber (eDestroyed);
  411. }
  412.  
  413. //• %subtitle 'Print the score'.
  414. void DrawScore ()
  415. {
  416.     TextMode (srcCopy);
  417.     MoveTo (scoreH, fieldHeight - 14);
  418.     TextSize (12);
  419.     DrawString ("\pScore: ");
  420.     MoveTo (scoreNumH, fieldHeight - 5);
  421.     TextSize (24);
  422.     DrawNumber (score);
  423.     disScore = score;
  424.  
  425.     TextSize (12);
  426.     MoveTo (statsH, fieldHeight - 20);
  427.     DrawString ("\p\pEnemy left: ");
  428.     DrawNumber (eLeft);
  429.     MoveTo (statsH, fieldHeight - 5);
  430.     DrawString ("\pYours left: ");
  431.     MoveTo (statsNumH, fieldHeight - 5);
  432.     DrawNumber (yLeft);
  433.     MoveTo (eDesH, fieldHeight - 20);
  434.     DrawString ("\pDestroyed: ");
  435.     MoveTo (eDesNumH, fieldHeight - 20);
  436.     DrawNumber (eDestroyed);
  437. }
  438.  
  439. //• %subtitle 'Print round #, score, and # killed'.
  440. void BottomLine ()
  441. {
  442.     TextSize (12);
  443.     RoundH = 10 + StringWidth ("\pRound: ");
  444.     scoreH = RoundH + 2 * StringWidth ("\p00 ");
  445.     scoreNumH = scoreH + StringWidth ("\pScore: ");
  446.     eDesH = fieldWidth - StringWidth ("\pDestroyed: 0000 ");
  447.     eDesNumH = fieldWidth - StringWidth ("\p0000 ");
  448.     statsH = eDesH - StringWidth ("\pEnemy left: 000 ");
  449.     statsNumH = eDesH - StringWidth ("\p000 ");
  450.     SetRect (&statsRect, statsNumH, fieldHeight - 30, eDesH - 2, fieldHeight);
  451.  
  452.     TextMode (srcCopy);
  453.     MoveTo (10, fieldHeight - 14);
  454.     DrawString ("\pRound: ");
  455.     MoveTo (RoundH, fieldHeight - 5);
  456.     TextSize (24);
  457.     DrawNumber (roundNumber);
  458.  
  459.     DrawScore ();
  460.     MoveTo (eDesH, fieldHeight - 5);
  461.     DrawString ("\pHigh score: ");
  462.     DrawNumber (highScore);
  463. }
  464.  
  465. //• %subtitle 'Create a new fireball'.
  466. void AllocFB (Point location)
  467. {
  468.     short i;
  469.     short idlefb;
  470.  
  471.     idlefb = 0;
  472.     for (i = 1; 1 <= maxFb; i++)
  473.         if ((fbs[i].mode == fbIdle) && (idlefb == 0))
  474.             idlefb = i;
  475.     
  476.     if (idlefb != 0)
  477.     {
  478.         SetRect (&fbs[idlefb].bounds, location.h - 30, location.v - 30, location.h + 30, location.v + 30);
  479.         fbs[idlefb].size = 0;
  480.         fbs[idlefb].mode = fbGrow;
  481.     }
  482. }
  483. //• of AllocFB.
  484.  
  485. //• %subtitle 'Advance (animate) the fireballs'.
  486. void AdvanFb ()
  487. {
  488.     short i;
  489.     Rect aRect;
  490.     Rect fbRect;        //• Rectangle for drawing fireballs.
  491.  
  492.     PenSize (fbRadRate, fbRadRate);
  493.     PenPat (&qd.black);
  494.     SetRect (&aRect, 0, 0, 60, 60);
  495.     for (i = 1; i < maxFb; i++)
  496.     {
  497.         if (fbs[i].mode != fbIdle)
  498.             switch (fbs[i].mode)
  499.             {
  500.                 case fbGrow: 
  501.                 {
  502.                     fbs[i].size = fbs[i].size + 1;
  503.                     fbBitMap.baseAddr = (Ptr) &fbBits[240 * fbs[i].size - 239];    //• Select a fireball picture.
  504.                     
  505.                     CopyBits ((BitMap*) &fbBitMap, 
  506.                              &qd.thePort->portBits, 
  507.                              &aRect, 
  508.                              &fbs[i].bounds, srcOr, 0L);
  509.                              
  510.                     if (fbs[i].size >= fbRad)
  511.                         fbs[i].mode = fbShrink;
  512.                 }
  513.                 break;
  514.                 
  515.                 case fbShrink: 
  516.                 {
  517.                     fbBitMap.baseAddr = (Ptr) &fbBits[240 * fbs[i].size - 239];    //• Select the correct fireball picture.
  518.  
  519.                     CopyBits ((BitMap*) &fbBitMap, 
  520.                              &qd.thePort->portBits, 
  521.                              &aRect, 
  522.                              &fbs[i].bounds, srcBic, 0L);
  523.                              
  524.                     fbs[i].size = fbs[i].size - 1;
  525.                     if (fbs[i].size == 0)
  526.                         fbs[i].mode = fbIdle;
  527.                 }
  528.                 break;
  529.             }
  530.     }    //• of loop.
  531. }
  532. //• of AdvanFb.
  533.  
  534. //• %subtitle 'Create a new enemy missile'.
  535. void AllocMS (Point position, short city, short kind)
  536. {
  537.     short i;
  538.     short idleMs;
  539.     short targetCity;
  540.     short targetH;
  541.  
  542.     idleMs = 0;
  543.     for (i = 1; i <= maxMs; i++)
  544.         if ((missiles[i].mode == msIdle) && (idleMs == 0))
  545.             idleMs = i;
  546.     
  547.     if (idleMs != 0)
  548.     {
  549.         i = idleMs;
  550.         missiles[i].start.v = position.v;    //• Set coordinates for beginning of path.
  551.         missiles[i].start.h = position.h;
  552.         missiles[i].pos.v = missiles[i].start.v * 16;    //• Initial current position is.
  553.         missiles[i].pos.h = missiles[i].start.h * 16; //•    at start of path.o.
  554.         missiles[i].dv = msSpeed;        //• Speed of missile.
  555.         missiles[i].city = city;
  556.         targetH = (((missiles[i].city * 2 - 1) * fieldWidth) / (2 * nCities)) * 16;
  557.         missiles[i].dh = (targetH - (missiles[i].start.h * 16)) / (((endv - missiles[i].start.v) * 16) / missiles[i].dv);
  558.         missiles[i].kind = kind;
  559.         missiles[i].mode = msActive;
  560.         nMissiles = nMissiles + 1;
  561.     }
  562. }
  563.  
  564. Boolean BlackPixel (Point aPoint)
  565. {
  566.     aPoint.h = aPoint.h / 16;
  567.     aPoint.v = aPoint.v / 16;
  568.     return ((GetPixel (aPoint.h, aPoint.v) && 
  569.              GetPixel (aPoint.h + 1, aPoint.v)));
  570. }
  571.  
  572. //• %subtitle 'Advance (animate) the enemy missiles'.
  573. void AdvanMs ()
  574. {
  575.     short i, j;
  576.     Point newPos, newPixel;
  577.     Boolean detonate;
  578.     short points;
  579.  
  580.     PenMode (patCopy);
  581.     for (i = 1; i <= maxMs; i++)
  582.     {
  583.         if (missiles[i].mode != msIdle)
  584.         {
  585.             newPos.v = missiles[i].pos.v + missiles[i].dv;
  586.             newPos.h = missiles[i].pos.h + missiles[i].dh;
  587.             newPixel.v = newPos.v / 16;
  588.             newPixel.h = newPos.h / 16;
  589.             detonate = false;
  590.             
  591.             if (BlackPixel (newPos) || BlackPixel (missiles[i].pos))
  592.                 detonate = true;
  593.             
  594.             PenSize (mWidth, mWidth);
  595.             PenPat (&qd.gray);
  596.             MoveTo (missiles[i].pos.h / 16, missiles[i].pos.v / 16);
  597.             LineTo (newPixel.h, newPixel.v);
  598.             missiles[i].pos = newPos;
  599.  
  600.             //• If it's a Mirv, and it's reached the right altitude,.
  601.             if ((missiles[i].kind == msMirv) && 
  602.                  (newPixel.v > mirvHeight))    
  603.             {                    //• then make lots of little missiles.
  604.                 for (j = 1; j <= nCities; j++)
  605.                     if ((j != missiles[i].city) && (Rnd (100) < mirvNasty))
  606.                         AllocMS (newPixel, j, msNormal);
  607.                 missiles[i].kind = msNormal;
  608.             }
  609.             //• If the missile has reached its target or 
  610.             //• hit a fireball...
  611.             if ((newPos.v / 16 >= endv) || detonate)    
  612.             {
  613.                 PenSize (mWidth + 2, mWidth + 2);
  614.                 PenPat (&qd.white);
  615.                 MoveTo (missiles[i].start.h - 1, missiles[i].start.v - 1);    //• Erase the line.
  616.                 LineTo (newPixel.h - 1, newPixel.v - 1);
  617.                 missiles[i].mode = msIdle;    //• turn off the missile,.
  618.                 nMissiles = nMissiles - 1;
  619.                 if (detonate)
  620.                 {
  621.                     if ((mFlag2 == 2) || ((mFlag2 == 1) && (Rnd (25) > roundNumber)))
  622.                         AllocFB (newPixel);        //• Make a new fireball.
  623.                     eDestroyed = eDestroyed + 1;
  624.                     score = score + MPoints[missiles[i].kind];
  625.                     UpdScore ();
  626.                 }
  627.                 else 
  628.                     if (cities[missiles[i].city])
  629.                     {
  630.                         AllocFB (newPixel);
  631.                         cities[missiles[i].city] = false;
  632.                         citiesLeft = citiesLeft - 1;
  633.                         if (citiesLeft + bCities == 0)
  634.                             gameOver = true;
  635.                     }
  636.             }    //• of detonate routine.
  637.         }    //• of THEN clause for this active missile.
  638.     }    //• of missile loop.
  639. }
  640. //• of AdvanMs.
  641.  
  642. //• %subtitle 'Print a string in the center of the window'.
  643. void Centre (Str255 theText, short posV)
  644. {
  645.     MoveTo ((fieldWidth - StringWidth (theText)) / 2, posV);
  646.     DrawString (theText);
  647. }
  648.  
  649. //• %subtitle 'Clear the screen'.
  650. void ClearScreen ()
  651. {
  652.     Rect aRect;
  653.  
  654.     aRect.top = 0;
  655.     aRect.left = 0;
  656.     aRect.bottom = fieldHeight;
  657.     aRect.right = fieldWidth;
  658.     FillRect (&aRect, &qd.white);
  659. }
  660.  
  661. //• %subtitle 'Draw a city'.
  662. void DrawCity (short city)
  663. {
  664.     Point cityLoc;
  665.     short bOffset;
  666.     Rect aRect;
  667.  
  668.     qd.randSeed = 2;    //• Make each city look the same.
  669.     cityLoc.v = fieldHeight - cityHeight;
  670.     cityLoc.h = ((city * 2 - 1) * fieldWidth) / (2 * nCities);
  671.     bOffset = - (cityWidth / 2);
  672.     while (bOffset < (cityWidth / 2))
  673.     {
  674.         aRect.left = cityLoc.h + bOffset;
  675.         aRect.top = cityLoc.v - Rnd (BuildHeight);
  676.         aRect.bottom = cityLoc.v + 2;
  677.         aRect.right = aRect.left + buildWidth;
  678.         FillRect (&aRect, &(**cityPattern));
  679.         bOffset = bOffset + buildWidth;
  680.     }
  681. }
  682.  
  683. //• %subtitle 'Draw cities and bottom line'.
  684. void DrawStuff ()
  685. {
  686.     short i;
  687.     Rect aRect;
  688.  
  689.     ClearScreen ();
  690.  
  691.     for (i = 1; i <= nCities; i++)
  692.         if (cities[i])
  693.             DrawCity (i);
  694.             
  695.     PenPat (&qd.black);
  696.     qd.randSeed = TickCount ();    //• Randomize.
  697.  
  698.     BottomLine ();
  699. }
  700. //• of DrawStuff.
  701.  
  702. //• %subtitle 'MinMax - Convert Num to String with range checking.
  703. short MinMax (Str255 theString, short minimum, short maximum)
  704. {
  705.     long theNumber;
  706.  
  707.     StringToNum (theString, &theNumber);
  708.     if (theNumber < minimum)
  709.         theNumber = minimum;
  710.     if (theNumber > maximum)
  711.         theNumber = maximum;
  712.     return (theNumber);
  713. }
  714.  
  715. //• %subtitle 'DoGameOptions - Does the big dialog box'.
  716. void DoGameOptions ()
  717. {
  718.     enum {        //• Here are all the magic constants that go with that monster dialog box:.
  719.         OK = 1,            //• OK button.
  720.         gSpeed = 5,        //• Game Speed text box.
  721.         sRound = 7,        //• Start Round text box.
  722.         MPB = 18,        //• MPBase text boxes.
  723.         MPE = 24,        //• MPExtra text boxes.
  724.         MF1 = 8,        //• mFlag1 check box.
  725.         MF2 = 10,        //• mFlag2 radio buttons.
  726.         MEX = 15        //• MExists check boxes.
  727.     };
  728.     
  729.     GrafPtr savePort;        //• For saving the GrafPort and restoring later.
  730.     DialogRecord dStorage;    //• Storage for my dialog box.
  731.     DialogPtr dPtr;            //• Pointer to my dialog box.
  732.     short itemHit;            //• Item that was just hit by the user.
  733.     short theType;            //• not used.
  734.     Handle theHandle;        //• Temp. handle.
  735.     Rect theRect;            //• not used.
  736.     short theValue;            //• Temp. integer value.
  737.     short i;                //• Loop Variable.
  738.     short station;            //• Current setting of radio buttons.
  739.     Str255 theString;        //• Temp. string.
  740.     Str255 theString1 = {"\p+"};        //• Temp. string.
  741.     Str255 theString2 = {"\p* round"};
  742.  
  743.     //• Load the data from disk.
  744.     dPtr = GetNewDialog (box2_id, &dStorage, (WindowPtr)-1L);    
  745.  
  746.     GetDItem (dPtr, gSpeed, &theType, &theHandle, &theRect);    //• Set up all the EditText boxes.
  747.     NumToString (gameSpeed, theString);
  748.     SetIText (theHandle, (StringPtr) theString);
  749.     GetDItem (dPtr, sRound, &theType, &theHandle, &theRect);
  750.     NumToString (startRound, theString);
  751.     SetIText (theHandle, (StringPtr) theString);
  752.  
  753.     for (i = 0; i < 2; i++)
  754.     {
  755.         GetDItem (dPtr, MPB + i, &theType, &theHandle, &theRect);    //• Text for points each missile is worth.
  756.         NumToString (MPBase[i + 1], theString);
  757.         SetIText (theHandle, theString);
  758.         GetDItem (dPtr, MPB + 3 + i, &theType, &theHandle, &theRect);    //• Text for plus sign.
  759. //        theString = '+';
  760.         SetIText (theHandle, theString1);
  761.         GetDItem (dPtr, MPE + i, &theType, &theHandle, &theRect);    //• Text for additional points each round.
  762.         NumToString (MPExtra[i + 1], theString);
  763.         SetIText (theHandle, theString);
  764.         GetDItem (dPtr, MPE + 3 + i, &theType, &theHandle, &theRect);    //• text : '* round'.
  765. //        theString = "\p* round";
  766.         SetIText (theHandle, theString2);
  767.         GetDItem (dPtr, MEX + i, &theType, &theHandle, &theRect);    //• Check boxes for types of missiles.
  768.         theValue = 0;
  769.         if (MExists[i + 1])
  770.             theValue = 1;
  771.         SetCtlValue ((ControlHandle) theHandle, theValue);
  772.     }
  773.     for (i = 0; i < 2; i++)
  774.     {    //• Program the radio buttons.
  775.         GetDItem (dPtr, MF2 + i, &theType, &theHandle, &theRect);
  776.         theValue = 0;
  777.         
  778.         if (mFlag2 == i)
  779.             theValue = 1;
  780.             
  781.         SetCtlValue ((ControlHandle) theHandle, theValue);
  782.     }
  783.     station = MF2 + mFlag2;
  784.  
  785.     GetDItem (dPtr, MF1, &theType, &theHandle, &theRect);    //• Set check box for mFlag1.
  786.     theValue = 0;
  787.     
  788.     if (mFlag1)
  789.         theValue = 1;
  790.         
  791.     SetCtlValue ((ControlHandle) theHandle, theValue);
  792.  
  793.     SelIText (dPtr, sRound, 0, 1000);    //• Initial selection is startRound.
  794.  
  795.     do
  796.     {
  797.         ModalDialog (0L, &itemHit);    //• Let them hit an item.
  798.  
  799.         if ((itemHit == 10)    ||    //• Was it one of the radio buttons?.
  800.             (itemHit == 11) ||
  801.             (itemHit == 12))
  802.         {
  803.             GetDItem (dPtr, station, &theType, &theHandle, &theRect);    //• Get the old one, and turn it off.
  804.             SetCtlValue ((ControlHandle) theHandle, 0);
  805.             station = itemHit;
  806.             GetDItem (dPtr, station, &theType, &theHandle, &theRect);    //• Turn this one on.
  807.             SetCtlValue ((ControlHandle) theHandle, 1);
  808.         }
  809.         if ((itemHit == 8)  ||    //• Was it a check box?.
  810.             (itemHit == 15) ||
  811.             (itemHit == 16) ||
  812.             (itemHit == 17))
  813.         {
  814.             GetDItem (dPtr, itemHit, &theType, &theHandle, &theRect);
  815.             theValue = 1 - GetCtlValue ((ControlHandle) theHandle);    //• Find the value and invert it.
  816.             SetCtlValue ((ControlHandle) theHandle, theValue);
  817.         }
  818.     } while (! (itemHit == 1) || ( itemHit == 2));
  819.  
  820.     if (itemHit == OK)
  821.     {    //• Have to get all the new values now!.
  822.         GetDItem (dPtr, gSpeed, &theType, &theHandle, &theRect);    //• Set up all the EditText boxes.
  823.         GetIText ((Handle) theHandle, theString);
  824.         gameSpeed = MinMax (theString, 1, 99);
  825.  
  826.         GetDItem (dPtr, sRound, &theType, &theHandle, &theRect);
  827.         GetIText ((Handle) theHandle, theString);
  828.         startRound = MinMax (theString, 1, 99);
  829.  
  830.         for (i = 0; i < 2; i++)
  831.         {
  832.             GetDItem (dPtr, MPB + i, &theType, &theHandle, &theRect);    //• Text for points each missile is worth.
  833.             GetIText (theHandle, theString);
  834.             MPBase[i + 1] = MinMax (theString, -1000, 1000);
  835.             GetDItem (dPtr, MPE + i, &theType, &theHandle, &theRect);    //• Text for additional points each round.
  836.             GetIText (theHandle, theString);
  837.             MPExtra[i + 1] = MinMax (theString, -1000, 1000);
  838.             GetDItem (dPtr, MEX + i, &theType, &theHandle, &theRect);    //• Check boxes for types of missiles.
  839.             if (GetCtlValue ((ControlHandle) theHandle) == 0)
  840.                 MExists[i + 1] = false;
  841.             else
  842.                 MExists[i + 1] = true;
  843.         }
  844.         for (i = 0; i < 2; i++)
  845.         {    //• radio buttons.
  846.             GetDItem (dPtr, MF2 + i, &theType, &theHandle, &theRect);
  847.             if (GetCtlValue ((ControlHandle) theHandle) == 1)
  848.                 mFlag2 = i;
  849.         }
  850.         GetDItem (dPtr, MF1, &theType, &theHandle, &theRect);    //• mFlag1.
  851.         if (GetCtlValue ((ControlHandle) theHandle) == 0)
  852.             mFlag1 = false;
  853.         else
  854.             mFlag1 = true;
  855.     }    //• if itemHit = OK.
  856.     DisposDialog (dPtr);
  857.     GetPort (&savePort);        //• save whatever port was current.
  858.     SetPort (myWindow);
  859.     InvalRect (&screenRect);    //• force the entire screen, including frame, to be redrawn.
  860.     SetPort ((GrafPtr) savePort);
  861. }
  862. //• of DoGameOptions.
  863.  
  864. //• %subtitle 'DoCommand - Does all the menu commands'.
  865. void DoCommand (long mResult)
  866. {
  867.     Str255 name;
  868.     short theMenu, theItem;    //• Menu and item numbers.
  869.     Boolean dummy;
  870.  
  871.     theMenu = HiWord (mResult);
  872.     theItem = LoWord (mResult);
  873.     switch (theMenu)
  874.     {
  875.  
  876.         case appleMenu:                //• About Missile and Desk Accessories.
  877.             if (theItem == 1)
  878.                 AboutMissile ();
  879.             else
  880.                 {
  881.                     GetItem (myMenus[1], theItem, name);
  882.                     refNum = OpenDeskAcc (name);
  883.             }
  884.         break;
  885.         
  886.         case fileMenu:    //• There is currently only one option in this menu.
  887.         {
  888.             doneFlag = true;
  889.             pauseFlag = false;
  890.             esFlag = false;
  891.         }
  892.         break;
  893.         
  894.         case editMenu:    //• Cut, copy, paste, and undo.
  895.         {
  896.             dummy = SystemEdit (theItem - 1);    //• Can't cut and paste in game window.
  897.         }    //• of editMenu.
  898.         break;
  899.         
  900.         case gameMenu: 
  901.         {
  902.         //• SetPort (myWindow);.
  903.             switch (theItem)
  904.             {
  905.                 case 1: 
  906.                     pauseFlag = true;    //• pause game.
  907.                 break;
  908.                 
  909.                 case 2: 
  910.                     pauseFlag = false;    //• resume game.
  911.                 break;
  912.                 
  913.                 case 3: //• Empty line in menu.
  914.                 break;
  915.                 
  916.                 case 4: 
  917.                     DoGameOptions ();
  918.                 break;
  919.                 
  920.                 case 5: 
  921.                 {                //• New Game.
  922.                     gameOver = true;
  923.                     endDelay = 100;
  924.                     esFlag = false;
  925.                     pauseFlag = false;
  926.                 }
  927.                 break;
  928.             }
  929.             break;    //• of item case.
  930.         }
  931.         break;
  932.     }    //• of menu case.
  933.     HiliteMenu (0);
  934. }
  935. //• of DoCommand.
  936.  
  937. //• %subtitle 'Routine to check for events'.
  938. void CheckEvents ()
  939. {
  940.     Point mousePoint;
  941.     short mouseCode;
  942.     char theChar;
  943.     Fixed i;
  944.     Rect fbRect;        //• Rectangle for drawing fireballs.
  945.  
  946.     do
  947.     {
  948.         SystemTask ();
  949.  
  950.         GetMouse (&mousePoint);
  951.         LocalToGlobal (&mousePoint);
  952.         mouseCode = FindWindow (mousePoint, &whichWindow);
  953.  
  954.         //• If my window is activated, set cursor:
  955.         if (FrontWindow () == myWindow)    
  956.  
  957.             //• If it's above my window, but not the scroll bar, 
  958.             //• the game is 'active',.my window is in front, and 
  959.             //• the user has missiles left, then:
  960.             if ((whichWindow == myWindow) && 
  961.                 (mouseCode != inMenuBar) && 
  962.                 (! pauseFlag) && 
  963.                 (FrontWindow () == myWindow) && 
  964.                 (yLeft > 0))    
  965. ;//                SetCursor (crosshairs)    //• Set the Missile Command cursor.
  966.             else
  967. ;//                SetCursor (crosshairs2);    //• Otherwise, use an arrow.
  968.  
  969.         while (GetNextEvent (everyEvent, &myEvent))
  970.             switch (myEvent.what)
  971.             {
  972.                 case mouseDown: 
  973.                 {
  974.                     code = FindWindow (myEvent.where, &whichWindow);
  975.                     switch (code)
  976.                     {
  977.                         case inMenuBar: 
  978.                             DoCommand (MenuSelect (myEvent.where));
  979.                         break;
  980.                         
  981.                         case inSysWindow: 
  982.                             SystemClick (&myEvent, whichWindow);
  983.                         break;
  984.  
  985.                         //• Can't drag game window - it's too hard to.
  986.                         //• figure out collisions off-screen.
  987.                         case inDrag: 
  988.                         break;
  989.                             
  990.                         case inGrow:
  991.                         case inContent: 
  992.                         {
  993.                             if ((whichWindow != FrontWindow ()) && (whichWindow != myWindow))    //• Can't select myWindow - must close other windows.
  994.                                 SelectWindow (whichWindow);
  995.                             else 
  996.                                 //• Game must be running.  
  997.                                 //• Make sure they have some missiles left.
  998.                                 if ((! pauseFlag) && (FrontWindow () == myWindow) && (yLeft > 0))    //• Can't make fireballs while paused.
  999.                                 {
  1000.                                     GlobalToLocal (&myEvent.where);
  1001.                                     if (myEvent.where.v > nukeHeight)    //• If it's too low,.
  1002.                                         myEvent.where.v = nukeHeight; //•    make it legal.
  1003.  
  1004.                                     //• Put a fireball in the list.
  1005.                                     AllocFB (myEvent.where);    
  1006.  
  1007.                                     //• They have less missiles now.
  1008.                                     yLeft = yLeft - 1;    
  1009.  
  1010.                                     //• let them know.
  1011.                                     UpdScore ();                
  1012.                                 }
  1013.                         }
  1014.                         break;
  1015.                     }
  1016.                     break;
  1017.                 }
  1018.                 break;
  1019.                 
  1020.                 //• No Autokey - don't want to repeat Option-N.
  1021.                 case keyDown:    
  1022.                     if (((myEvent.modifiers / 256) % 2 != 0))    //• If command key was held down.
  1023.                     {
  1024.                         theChar = (char) myEvent.message % 256;
  1025.                         DoCommand (MenuKey (theChar));
  1026.                     }
  1027.                 break;
  1028.                 
  1029.                 case updateEvt: 
  1030.                 {
  1031.                     SetPort (myWindow);
  1032.                     BeginUpdate (myWindow);
  1033.                     if (playing)
  1034.                     {
  1035.                         for (i = 1; i <= nCities; i++)
  1036.                             if (cities[i])
  1037.                             {
  1038.                                 DrawCity (i);
  1039.                             }
  1040.                             //• Randomize after drawing cities.
  1041.                             qd.randSeed = TickCount ();    
  1042.                             for (i = 1; i <= maxMs; i++)
  1043.                                 if (missiles[i].mode == msActive)
  1044.                                 {
  1045.                                     PenSize (mWidth, mWidth);
  1046.                                     PenPat (&qd.gray);
  1047.                                     MoveTo (missiles[i].start.h, missiles[i].start.v);
  1048.                                     LineTo (missiles[i].pos.h / 16, missiles[i].pos.v / 16);
  1049.                                 }
  1050.                                 for (i = 1; i <= maxFb; i++)
  1051.                                     if (fbs[i].mode != fbIdle)
  1052.                                     {
  1053.                                         fbRect = fbs[i].bounds;
  1054.                                         InsetRect (&fbRect, 30 - 2 * fbs[i].size, 30 - 2 * fbs[i].size);
  1055.                                         PenMode (patCopy);
  1056.                                         FillOval (&fbRect, &qd.black);
  1057.                                     }
  1058.                     }
  1059.                     BottomLine ();
  1060.                     EndUpdate (myWindow);
  1061.                 }
  1062.                 break;
  1063.             }    //• of event case.
  1064.  
  1065.     //• If another window has been selected, don't do anything 
  1066.     //• except process events until game window is re-selected.    .
  1067.     } while (! (FrontWindow () == myWindow) && ! pauseFlag);    
  1068. }
  1069. //• of CheckEvents.
  1070.  
  1071. //• %subtitle 'Print the GAME OVER message'.
  1072. void EndScreen ()
  1073. {
  1074.     Rect aRect;
  1075.     short i;
  1076.     short GameOffset, OverOffset;
  1077.     Point Center;
  1078.  
  1079.     TextSize (72);
  1080.     TextMode (srcBic);
  1081.     PenMode (patOr);
  1082.     PenPat (&qd.black);
  1083.     PenSize (5, 5);
  1084.  
  1085.     GameOffset = StringWidth ("\pGAME") / 2;
  1086.     OverOffset = StringWidth ("\pOVER") / 2;
  1087.     Center.v = fieldHeight / 2;
  1088.     Center.h = fieldWidth / 2;
  1089.  
  1090.     SetRect (&aRect, Center.h, Center.v, Center.h, Center.v);
  1091.     for (i = 1; i <= 30; i++)
  1092.     {
  1093.         CheckEvents ();
  1094.         InsetRect (&aRect, -5, -5);
  1095.         FrameOval (&aRect);
  1096.         TextSize (72);
  1097.         TextMode (srcBic);
  1098.         Centre ("\pGAME", Center.v - 12);
  1099.         Centre ("\pOVER", Center.v + 60);
  1100.     }
  1101.     PenMode (patBic);
  1102.     for (i = 1; i <= 30; i++)
  1103.     {
  1104.         CheckEvents ();
  1105.         FrameOval (&aRect);
  1106.         InsetRect (&aRect, 5, 5);
  1107.     }
  1108. }
  1109. //• of EndScreen.
  1110.  
  1111. void InitRound ()
  1112. {
  1113.     short i;
  1114.     Boolean roundOver;
  1115.     short rMissiles;    //• "round missiles" - number to set eLeft to each round.
  1116.     short yMissiles;    //• "your Missiles" - similar to rMissiles.
  1117.     
  1118.     roundOver = false;
  1119.     endDelay = 0;
  1120.     playing = true;
  1121.     DrawStuff ();    //• Redraw screen.
  1122.     for (i = 1; i < maxFb; i++)
  1123.         fbs[i].mode = fbIdle;
  1124.  
  1125.     for (i = 1; i < maxMs; i++)
  1126.         missiles[i].mode = msIdle;
  1127.  
  1128.     for (i = 1; i < 3; i++)
  1129.         MPoints[i] = MPBase[i] + MPExtra[i] * roundNumber;
  1130.  
  1131.     for (i = 1; i < nCities; i++)
  1132.         cities2[i] = cities[i];
  1133.  
  1134.     nMissiles = 0;
  1135.     mirvRate = 0;
  1136.     mirvHeight = fieldHeight / 4;
  1137.     mirvNasty = 30;
  1138.     msSpeed = 60;
  1139.     
  1140.     if (roundNumber > 2)
  1141.         mirvRate = 10;
  1142.     if (roundNumber > 4)
  1143.         msSpeed = 80;
  1144.     if (roundNumber > 6)
  1145.         mirvRate = 20;
  1146.     if (roundNumber > 8)
  1147.         mirvNasty = 50;
  1148.     if (roundNumber > 10)
  1149.         mirvHeight = fieldHeight / 3;
  1150.     if (roundNumber > 12)
  1151.         msSpeed = 90;
  1152.     if (roundNumber > 14)
  1153.         mirvRate = 30;
  1154.     if (roundNumber > 19)
  1155.         msSpeed = 100;
  1156.         
  1157.     msRate = 5 + roundNumber;
  1158.     rMissiles = (roundNumber * roundNumber) / 6 + roundNumber + 6;    //• Compute # of enemy missiles.
  1159.     yMissiles = rMissiles + roundNumber;            //• Adjust yMissiles accordingly.
  1160.     eLeft = rMissiles;
  1161.     yLeft = yMissiles;
  1162.     roundNumber = roundNumber + 1;
  1163.     
  1164.     if ((roundNumber % 5) == 0)
  1165.         bCities = bCities + 1;
  1166.         
  1167.     BottomLine ();
  1168.     FlushEvents (everyEvent, 0);    //• Ignore unprocessed events from previous round.
  1169. }
  1170.  
  1171. void EndBonus ()
  1172. {
  1173.     short i, points;
  1174.     Str255 aString = {"\pEnd of round "}, s;
  1175.     
  1176.     ClearScreen ();
  1177.     BottomLine ();
  1178.     TextSize (24);
  1179.     TextMode (srcCopy);
  1180. //    aString = "End of round ";
  1181.     aString[15] = (char) (48 + (roundNumber % 10));
  1182.     
  1183.     if (roundNumber > 9)
  1184.         aString[14] = (char) (48 + (roundNumber / 10));
  1185.     
  1186.     if (citiesLeft > 0)
  1187.     {    //• give bonus points for cities.
  1188.         Centre (aString, 100);
  1189.         Centre ("\pBonus:", 130);
  1190.         points = 0;
  1191.         for (i = 1; i <= nCities; i++)
  1192.             if ((cities[i] && ! doneFlag))
  1193.             {
  1194.                 lastTick = TickCount ();
  1195.                 DrawCity (i);
  1196.                 points = points + roundNumber * 20;
  1197.                 score = score + roundNumber * 20;
  1198.                 TextSize (24);
  1199.                 NumToString (points, s);
  1200.                 Centre (s, 160);
  1201.                 UpdScore ();
  1202.                 do
  1203.                 {
  1204.                     CheckEvents ();
  1205.                 } while (! (TickCount () >= lastTick + 30) || doneFlag);
  1206.             }
  1207.     }
  1208.     if ((bCities > 0) && (citiesLeft < 6))
  1209.     {    //• Give a bonus city.
  1210.         lastTick = TickCount ();
  1211.         TextSize (24);
  1212.         Centre ("\p* Bonus City *", 190);
  1213.         citiesLeft = citiesLeft + 1;
  1214.         bCities = bCities - 1;
  1215.         do
  1216.         {
  1217.             i = Rnd (6);
  1218.         } while (cities[i]);
  1219.         cities[i] = true;
  1220.         do
  1221.         {
  1222.             CheckEvents ();
  1223.         } while (! (TickCount () >= lastTick + 60) || doneFlag);
  1224.     }
  1225. }
  1226. //• of EndBonus.
  1227.  
  1228. //• %subtitle 'Play a single game'.
  1229. void PlayGame ()
  1230. {
  1231.     short i;
  1232.     Str255 s;
  1233.     Point msPoint;
  1234.     short msCity, msKind;
  1235.     short rMissiles;    //• "round missiles" - number to set eLeft to each round.
  1236.     short yMissiles;    //• "your Missiles" - similar to rMissiles.
  1237.     Boolean roundOver;
  1238.     Str255 aString;
  1239.     short points;
  1240.     
  1241.     roundNumber = startRound;    //• Start out at this round.
  1242.     score = 0;
  1243.     eDestroyed = 0;
  1244.  
  1245.     for (i = 1; i < nCities; i++)
  1246.         cities[i] = true;
  1247.     citiesLeft = nCities;
  1248.     bCities = 0;
  1249.  
  1250.     nukeHeight = fieldHeight - cityHeight - (BuildHeight / 2) - fbRad * fbRadRate;
  1251.     endv = fieldHeight - cityHeight - (BuildHeight / 2);
  1252.  
  1253.     gameOver = false;
  1254.     esFlag = true;
  1255.     lastTick = TickCount ();
  1256.  
  1257.     do
  1258.     {    //• Repeat loop for playing rounds.
  1259.         InitRound ();
  1260.  
  1261.         do
  1262.         {    //• Repeat loop for animating objects in game.
  1263.             CheckEvents ();
  1264.  
  1265.             //• If enough time has elapsed since the last update, 
  1266.             //• update objects on screen.
  1267.             if (TickCount () >= lastTick + gameSpeed)    
  1268.             {
  1269.                 lastTick = TickCount ();
  1270.                 if ((eLeft <= 0) && (nMissiles == 0))
  1271.                     roundOver = true;
  1272.             
  1273.                     if (roundOver || gameOver)
  1274.                     endDelay = endDelay + 1;
  1275.                 else if ((Rnd (100) < msRate) && (eLeft > 0))
  1276.                     {            //• Launch an enemy missile every now and then.
  1277.                         eLeft = eLeft - 1;
  1278.                         msPoint.v = 0;
  1279.                         msPoint.h = Rnd (fieldWidth - 4);
  1280.                         do
  1281.                         {
  1282.                             msCity = Rnd (nCities);    //• Pick a city.
  1283.  
  1284.                         //• Try again for most missiles if city was destroyed.
  1285.                         } while (! ((Rnd (3) == 1) && mFlag1) || cities2[msCity]);    
  1286.         
  1287.                         msKind = 0;
  1288.                         if (MExists[msNormal])
  1289.                             msKind = msNormal;
  1290.                         if ((Rnd (100) <= mirvRate) && MExists[msMirv])
  1291.                             msKind = msMirv;
  1292.                         if (msKind == 0)
  1293.                             msKind = msNormal;
  1294.                         AllocMS (msPoint, msCity, msKind);
  1295.                 }
  1296.                 AdvanFb ();        //• Advance state of fireballs.
  1297.                 AdvanMs ();        //• Advance position of missiles.
  1298.             }
  1299.         } while (! ((gameOver || roundOver) && (endDelay > 30)) || doneFlag);
  1300.         playing = false;
  1301.         if (esFlag && (citiesLeft + bCities > 0) && ! doneFlag)
  1302.             EndBonus ();
  1303.     } while (! gameOver || doneFlag);    //• End of loop to play rounds.
  1304.     if (score > highScore)
  1305.         highScore = score;
  1306.     if (esFlag)
  1307.         EndScreen ();
  1308. }
  1309. //• of PlayGame.
  1310.  
  1311. //• %subtitle 'Main program'.
  1312.  
  1313. void main ()
  1314. {    //• main program.
  1315.     SetUp ();
  1316.  
  1317.     do
  1318.     {
  1319.         PlayGame ();
  1320.     } while (! doneFlag);
  1321. }
  1322.  
  1323.